package com.easylive.service.impl;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.annotation.Resource;

import com.easylive.entity.component.EsSearchComponent;
import com.easylive.entity.component.RedisComponent;
import com.easylive.entity.config.AppConfig;
import com.easylive.entity.constants.Constants;
import com.easylive.entity.dto.SysSettingDto;
import com.easylive.entity.dto.UploadingFileDto;
import com.easylive.entity.enums.*;
import com.easylive.entity.po.*;
import com.easylive.entity.query.*;
import com.easylive.exception.BusinessException;
import com.easylive.mappers.*;
import com.easylive.utils.CopyTools;
import com.easylive.utils.FFmpegUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.http.impl.nio.reactor.ExceptionEvent;
import org.springframework.stereotype.Service;
import com.easylive.entity.vo.PaginationResultVO;
import com.easylive.service.VideoInfoPostService;
import com.easylive.utils.StringTools;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;


/**
 * 视频信息（审核用） 业务接口实现
 */
@Service("videoInfoPostService")
@Slf4j
public class VideoInfoPostServiceImpl implements VideoInfoPostService {

	private ExecutorService executorService = Executors.newFixedThreadPool(10);

	@Resource
	private VideoInfoPostMapper<VideoInfoPost, VideoInfoPostQuery> videoInfoPostMapper;

	@Resource
	private RedisComponent redisComponent;

	@Resource
	private AppConfig appConfig;

	@Resource
	private FFmpegUtils fFmpegUtils;

	@Resource
	private VideoInfoFilePostMapper<VideoInfoFilePost, VideoInfoFilePostQuery> videoInfoFilePostMapper;

	@Resource
	private VideoInfoMapper<VideoInfo, VideoInfoQuery> videoInfoMapper;

	@Resource
	private VideoInfoFileMapper<VideoInfoFile, VideoInfoFileQuery> videoInfoFileMapper;

	@Resource
	private UserInfoMapper<UserInfo, UserInfoQuery> userInfoMapper;

	@Resource
	private VideoDanmuMapper<VideoDanmu, VideoDanmuQuery> videoDanmuMapper;

	@Resource
	private VideoCommentMapper<VideoComment, VideoCommentQuery> videoCommentMapper;

	@Resource
	private EsSearchComponent esSearchComponent;

	/**
	 * 根据条件查询列表
	 */
	@Override
	public List<VideoInfoPost> findListByParam(VideoInfoPostQuery param) {
		return this.videoInfoPostMapper.selectList(param);
	}

	/**
	 * 根据条件查询列表
	 */
	@Override
	public Integer findCountByParam(VideoInfoPostQuery param) {
		return this.videoInfoPostMapper.selectCount(param);
	}

	/**
	 * 分页查询方法
	 */
	@Override
	public PaginationResultVO<VideoInfoPost> findListByPage(VideoInfoPostQuery param) {
		int count = this.findCountByParam(param);
		int pageSize = param.getPageSize() == null ? PageSize.SIZE15.getSize() : param.getPageSize();

		SimplePage page = new SimplePage(param.getPageNo(), count, pageSize);
		param.setSimplePage(page);
		List<VideoInfoPost> list = this.findListByParam(param);
		PaginationResultVO<VideoInfoPost> result = new PaginationResultVO(count, page.getPageSize(), page.getPageNo(), page.getPageTotal(), list);
		return result;
	}

	/**
	 * 新增
	 */
	@Override
	public Integer add(VideoInfoPost bean) {
		return this.videoInfoPostMapper.insert(bean);
	}

	/**
	 * 批量新增
	 */
	@Override
	public Integer addBatch(List<VideoInfoPost> listBean) {
		if (listBean == null || listBean.isEmpty()) {
			return 0;
		}
		return this.videoInfoPostMapper.insertBatch(listBean);
	}

	/**
	 * 批量新增或者修改
	 */
	@Override
	public Integer addOrUpdateBatch(List<VideoInfoPost> listBean) {
		if (listBean == null || listBean.isEmpty()) {
			return 0;
		}
		return this.videoInfoPostMapper.insertOrUpdateBatch(listBean);
	}

	/**
	 * 多条件更新
	 */
	@Override
	public Integer updateByParam(VideoInfoPost bean, VideoInfoPostQuery param) {
		StringTools.checkParam(param);
		return this.videoInfoPostMapper.updateByParam(bean, param);
	}

	/**
	 * 多条件删除
	 */
	@Override
	public Integer deleteByParam(VideoInfoPostQuery param) {
		StringTools.checkParam(param);
		return this.videoInfoPostMapper.deleteByParam(param);
	}

	/**
	 * 根据VideoId获取对象
	 */
	@Override
	public VideoInfoPost getVideoInfoPostByVideoId(String videoId) {
		return this.videoInfoPostMapper.selectByVideoId(videoId);
	}

	/**
	 * 根据VideoId修改
	 */
	@Override
	public Integer updateVideoInfoPostByVideoId(VideoInfoPost bean, String videoId) {
		return this.videoInfoPostMapper.updateByVideoId(bean, videoId);
	}

	/**
	 * 根据VideoId删除
	 */
	@Override
	public Integer deleteVideoInfoPostByVideoId(String videoId) {
		return this.videoInfoPostMapper.deleteByVideoId(videoId);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void saveVideoInfoPost(VideoInfoPost videoInfoPost, List<VideoInfoFilePost> uploadFilePostList) {
		/**
		 * 系统配置：视频总分片数
		 */
		Integer videoPCount = redisComponent.getSysSetting().getVideoPCount();
		if (uploadFilePostList.size() > videoPCount) {
			throw new BusinessException(ResponseCodeEnum.CODE_600);
		}
		String videoId = videoInfoPost.getVideoId();
		// 修改参数校验
		if (!StringTools.isEmpty(videoId)) {
			VideoInfoPost dbVideoInfoPost = videoInfoPostMapper.selectByVideoId(videoInfoPost.getVideoId());
			if (ObjectUtils.isEmpty(dbVideoInfoPost)) {
				throw new BusinessException(ResponseCodeEnum.CODE_600);
			}
			/**
			 * 转码中和审核中不嫩修改
			 */
			VideoStatusEnum videoStatusEnum = VideoStatusEnum.getByStatus(dbVideoInfoPost.getStatus());
			if (videoStatusEnum.equals(VideoStatusEnum.STATUS0) || videoStatusEnum.equals(VideoStatusEnum.STATUS2)) {
				throw new BusinessException(ResponseCodeEnum.CODE_600);
			}
		}

		Date curDate = new Date();
		List<VideoInfoFilePost> deleteFileList = new ArrayList<>();
		List<VideoInfoFilePost> addFileList = uploadFilePostList;
		if (StringTools.isEmpty(videoId)) {
			// 新增
			videoId = StringTools.getRandomString(Constants.LENGTH_10);
			videoInfoPost.setVideoId(videoId);
			videoInfoPost.setCreateTime(curDate);
			videoInfoPost.setLastUpdateTime(curDate);
			videoInfoPost.setStatus(VideoStatusEnum.STATUS0.getStatus());
			videoInfoPostMapper.insert(videoInfoPost);
		} else {
			// 修改
			/**
			 * 查出数据中已经有的文件，然后与传入的做对比，看看有什么差异
			 */
			VideoInfoFilePostQuery filePostQuery = new VideoInfoFilePostQuery();
			filePostQuery.setVideoId(videoId);
			filePostQuery.setUserId(videoInfoPost.getUserId());
			List<VideoInfoFilePost> dbVideoFilePostList = videoInfoFilePostMapper.selectList(filePostQuery);
			// 将数据库中查出的记录转换成map，即当前已经上传的文件
			/**
			 * 将传过来的文件转换成map，即要上传的文件
			 * i -> i.getUploadId() 键生成器，uploadId
			 * Function.identity() 值生成器，返回原值
			 * (data1, data2) -> data2 合并函数，处理键冲突，如果有多个重复的键，会选择后一个，后一个覆盖前一个的值
			 */
			Map<String, VideoInfoFilePost> uploadFileMap =
					uploadFilePostList.stream().collect(
							Collectors.toMap(i -> i.getUploadId(), Function.identity(), (data1, data2) -> data2));

			Boolean updateFileName = false;
			/**
			 * 遍历数据库中的文件，传过来的跟数据库里的做对比
			 */
			for (VideoInfoFilePost filePost : dbVideoFilePostList) {
				VideoInfoFilePost updateFile = uploadFileMap.get(filePost.getUploadId());
				// 如果传过来的文件中没有这个文件（说明这个文件是新增的），则删除数据库中的文件
				if (ObjectUtils.isEmpty(updateFile)) {
					deleteFileList.add(filePost);
				} else if (!Objects.equals(updateFile.getFileName(), filePost.getFileName())) {
					// 如果传过来的文件有，但是文件名不一样，则更新数据库中的文件名
					updateFileName = true;
				}
			}
			// 遍历传过来的文件，找出fildId为空的，即要新增的文件
			addFileList = uploadFilePostList.stream().filter(i -> i.getFileId() == null).collect(Collectors.toList());

			// 修改的时候，更改最后更新时间
			videoInfoPost.setLastUpdateTime(curDate);

			/**
			 * 如果修改了，则需要改变审核状态
			 */
			Boolean changeVideoInfo = this.changeVideoInfo(videoInfoPost);
			if (addFileList != null && !addFileList.isEmpty()) {
				// 如果新增了文件，需要改变为转码中状态，如果是删除文件，则不需要改变状态
				videoInfoPost.setStatus(VideoStatusEnum.STATUS0.getStatus());
			} else if (changeVideoInfo || updateFileName) {
				// 修改了视频信息或者文件名，改为审核中
				videoInfoPost.setStatus(VideoStatusEnum.STATUS2.getStatus());
			}

			videoInfoPostMapper.updateByVideoId(videoInfoPost, videoId);
		}

		// 删除要删除的文件
		if (!deleteFileList.isEmpty()) {
			List<String> deleteFileIdList = deleteFileList.stream().map(i -> i.getFileId()).collect(Collectors.toList());
			// 删除数据库中的记录
			videoInfoFilePostMapper.deleteBatchByFileId(deleteFileIdList, videoInfoPost.getUserId());
			// 利用 redis 消息队列，删除文件
			/**
			 * 删除文件是个很费时间的操作，所以使用redis消息队列异步处理
			 */
			List<String> deleteFilePathList = deleteFileList.stream().map(i -> i.getFilePath()).collect(Collectors.toList());
			redisComponent.addFile2DelMQ(videoId, deleteFilePathList);
		}

		// 更新或插入文件记录表
		Integer index = 1;
		for (VideoInfoFilePost filePost : uploadFilePostList) {
			filePost.setFileIndex(index++);
			filePost.setVideoId(videoId);
			filePost.setUserId(videoInfoPost.getUserId());
			if (StringTools.isEmpty(filePost.getFileId())) {
				// 新增的
				filePost.setFileId(StringTools.getRandomString(Constants.LENGTH_20));
				filePost.setUpdateType(VideoFileUpdateTypeEnum.UPDATED.getCode());
				filePost.setTransferResult(VideoFileTransferResultEnum.TRANSFERRING.getCode());
			}
		}

		videoInfoFilePostMapper.insertOrUpdateBatch(uploadFilePostList);

		/**
		 * addFileList 在新增的情况下，默认就是传过来的文件
		 */
		if (!ObjectUtils.isEmpty(addFileList) && !addFileList.isEmpty()) {
			// 加到消息队列中
			for (VideoInfoFilePost filePost : addFileList) {
				filePost.setVideoId(videoId);
				filePost.setUserId(videoInfoPost.getUserId());
			}
			redisComponent.addFile2TransferMQ(addFileList);
		}

	}

	/**
	 * 看是否修改了视频信息
	 * @param videoInfoPost 页面上传过来的视频信息
	 * @return
	 */
	private Boolean changeVideoInfo(VideoInfoPost videoInfoPost) {
		VideoInfoPost dbVideoInfoPost = videoInfoPostMapper.selectByVideoId(videoInfoPost.getVideoId());

		// 标题，封面，简介，标签
		if (!Objects.equals(videoInfoPost.getVideoName(), dbVideoInfoPost.getVideoName()) ||
		    !Objects.equals(videoInfoPost.getVideoCover(), dbVideoInfoPost.getVideoCover()) ||
		    !Objects.equals(videoInfoPost.getIntroduction(), dbVideoInfoPost.getIntroduction() == null ? "" : dbVideoInfoPost.getIntroduction()) ||
			!Objects.equals(videoInfoPost.getTags(), dbVideoInfoPost.getTags())
		    ) {
			return true;
		}
		return false;
	}

	/**
	 * 处理文件，使用ffmpeg将切片转码成mp4
	 * @param videoInfoFilePost
	 */
	@Override
	public void transferVideoFile(VideoInfoFilePost videoInfoFilePost) {
		VideoInfoFilePost uploadFilePost = new VideoInfoFilePost();
		try {
			// 获取文件信息，拿到路径
			UploadingFileDto uploadFileDto = redisComponent.getUploadFileDto(videoInfoFilePost.getUserId(), videoInfoFilePost.getUploadId());

			// 获取临时路径
			String tempFilePath = appConfig.getProjectFolder() + Constants.FILE_FOLDER + Constants.FILE_TEMP + uploadFileDto.getFilePath();
			File tempFolder = new File(tempFilePath);

			// 转换到目标路径中
			String targetFilePath = appConfig.getProjectFolder() + Constants.FILE_FOLDER + Constants.FILE_VIDEO + uploadFileDto.getFilePath();
			File targetFolder = new File(targetFilePath);

			FileUtils.copyDirectory(tempFolder, targetFolder);

			// 删除临时文件夹以及redis中的记录
			FileUtils.forceDelete(tempFolder);
			redisComponent.deleteUploadFileDto(videoInfoFilePost.getUserId(), videoInfoFilePost.getUploadId());

			// 合并目标文件（都是分片）
			String completeVideo = targetFilePath + Constants.TEMP_VIDEO_NAME;
			union(targetFilePath, completeVideo, true);

			/**
			 * 通过ffmpeg获取播放时长
			 */
			Integer duration = fFmpegUtils.getVideoInfoDuration(completeVideo);
			// 赋值
			uploadFilePost.setDuration(duration);
			uploadFilePost.setFileSize(new File(completeVideo).length());
			uploadFilePost.setFilePath(Constants.FILE_VIDEO + uploadFileDto.getFilePath());
			uploadFilePost.setTransferResult(VideoFileTransferResultEnum.SUCCESS.getCode());

			/**
			 * 把合并后的 mp4文件转换成 ts文件，真正播放的是 ts文件
			 */
			convertVideo2Ts(completeVideo);

		} catch (Exception e) {
			log.error("转码失败", e);
			uploadFilePost.setTransferResult(VideoFileTransferResultEnum.FAILED.getCode());
		} finally {
			// 更新数据库中的记录
			videoInfoFilePostMapper.updateByUploadIdAndUserId(uploadFilePost, videoInfoFilePost.getUploadId(), videoInfoFilePost.getUserId());

			VideoInfoFilePostQuery filePostQuery = new VideoInfoFilePostQuery();
			filePostQuery.setVideoId(videoInfoFilePost.getVideoId());
			filePostQuery.setTransferResult(VideoFileTransferResultEnum.FAILED.getCode());
			Integer failCount = videoInfoFilePostMapper.selectCount(filePostQuery);
			if (failCount > 0) {
				// 如果有失败的，更新视频状态
				VideoInfoPost videoUpdate = new VideoInfoPost();
				videoUpdate.setStatus(VideoStatusEnum.STATUS1.getStatus());
				videoInfoPostMapper.updateByVideoId(videoUpdate, videoInfoFilePost.getVideoId());
				return;
			}
			// 再查转码中的
			filePostQuery.setTransferResult(VideoFileTransferResultEnum.TRANSFERRING.getCode());
			Integer transferringCount = videoInfoFilePostMapper.selectCount(filePostQuery);
			if (transferringCount == 0) {
				// 算总共的时间，比如有两个分p，每个分p有10秒，那么总共有20秒
				Integer duration = videoInfoFilePostMapper.sumDuration(videoInfoFilePost.getVideoId());
				// 此时已经转码成功，变成审核中
				VideoInfoPost videoUpdate = new VideoInfoPost();
				videoUpdate.setStatus(VideoStatusEnum.STATUS2.getStatus());
				videoUpdate.setDuration(duration);
				videoInfoPostMapper.updateByVideoId(videoUpdate, videoInfoFilePost.getVideoId());
			}
		}
	}

	/**
	 * 最终只保留 .ts 切片文件和 m3u8索引文件
	 * @param completeVideo
	 */
	private void convertVideo2Ts(String completeVideo) {
		File videoFile = new File(completeVideo);
		// ts文件的存放路径
		File tsFolder = videoFile.getParentFile();
		String codec = fFmpegUtils.getVideoCodec(completeVideo);
		// hevc编码的是一种高压缩率的编码格式，需要转换成h264，因为有的浏览器不支持hevc
		if (Constants.VIDEO_CODE_HEVC.equals(codec)) {
			/**
			 * 比如有一个原视频，叫 1.mp4，然后copy成了 1_temp.mp4，
			 * 又把 1_temp.mp4 转码成了 1.mp4 就相当于把最初的 hevc编码格式的1.mp4文件转换成了 mp4编码格式的1.mp4文件
			 * 最后再把temp文件删除掉
			 */
			String newFileName = completeVideo + Constants.VIDEO_TEMP_FILE_SUFFIX;
			// 相当于把这个文件copy成一个新的文件
			new File(completeVideo).renameTo(new File(newFileName));
			fFmpegUtils.convertHevc2Mp4(newFileName, completeVideo);
			// 删除临时文件
			new File(newFileName).delete();
		}
		fFmpegUtils.convertVideo2Ts(tsFolder, completeVideo);
		videoFile.delete();
	}

	/**
	 * 将临时目录中的文件拷贝到正式目录中，然后删除临时目录，将正式目录中的视频合并到当前目录下，.mp4
	 * @param ditPath  目标文件（分片文件）   xxxx/xxxxx
	 * @param toFilePath 合并生成文件的地址   xxxx/temp.mp4
	 * @param delSource  是否要删除分片文件
	 */
	private void union(String ditPath, String toFilePath, Boolean delSource) {
		File dir = new File(ditPath);
		if (!dir.exists()) {
			throw new BusinessException("目录不存在");
		}
		File[] fileList = dir.listFiles();
		File targetFile = new File(toFilePath);
		try (RandomAccessFile writeFile = new RandomAccessFile(targetFile, "rw")) {
			byte[] b = new byte[1024 * 1024];
			for (int i = 0; i < fileList.length; i++) {
				int len = -1;
				File chunkFile = new File(ditPath + File.separator + i);
				RandomAccessFile readFile = null;
				try {
					readFile = new RandomAccessFile(chunkFile, "r");
					while ((len = readFile.read(b)) != -1) {
						writeFile.write(b, 0, len);
					}
				} catch (Exception e) {
					log.error("合并分片失败", e);
					throw new BusinessException("合并分片失败");
				} finally {
					if (readFile!= null) {
						readFile.close();
					}
				}
			}

		} catch (Exception e) {
			log.error("合并文件失败", e);
			throw new BusinessException("合并文件失败");
		} finally {
			if (delSource) {
				for (int i = 0; i < fileList.length; i++) {
					fileList[i].delete();
				}
			}
		}
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void auditVideo(String videoId, Integer status, String reason) {
		VideoInfoPost videoInfoPost = videoInfoPostMapper.selectByVideoId(videoId);
		VideoStatusEnum videoStatusEnum = VideoStatusEnum.getByStatus(status);
		/**
		 * 假如没查找视频，或者审核状态不是2的，或者审核不通过并且没写原因，请求参数错误
		 *
		 */
		if (ObjectUtils.isEmpty(videoInfoPost) || (Objects.equals(videoStatusEnum, VideoStatusEnum.STATUS4)
													&& StringTools.isEmpty(reason))) {
			throw new BusinessException(ResponseCodeEnum.CODE_600);
		}
		// 更新状态
		VideoInfoPost updateVideo = new VideoInfoPost();
		updateVideo.setStatus(status);
		VideoInfoPostQuery videoInfoPostQuery = new VideoInfoPostQuery();
		videoInfoPostQuery.setStatus(VideoStatusEnum.STATUS2.getStatus());
		videoInfoPostQuery.setVideoId(videoId);

		/**
		 * 这里需要考虑乐观锁问题，必须是从审核中转换为其他状态，因此更新时需要加上 where state = 2 条件
		 */
		Integer updateCount = videoInfoPostMapper.updateByParam(updateVideo, videoInfoPostQuery);
		if (updateCount == 0) {
			throw new BusinessException("请检查视频是否已经审核，请刷新后重试");
		}
		videoInfoPost.setStatus(status);

		/**
		 * 需要把file改成没有更新的状态
		 */
		VideoInfoFilePost updateFile = new VideoInfoFilePost();
		updateFile.setUpdateType(VideoFileUpdateTypeEnum.NO_UPDATE.getCode());

		VideoInfoFilePostQuery filePostQuery = new VideoInfoFilePostQuery();
		filePostQuery.setVideoId(videoId);
		videoInfoFilePostMapper.updateByParam(updateFile, filePostQuery);

		/**
		 * 审核不通过，直接return
		 */
		if (status.equals(VideoStatusEnum.STATUS4.getStatus())) {
			return;
		}

		/**
		 * 需要判断用户是不是第一次审核通过，如果是，需要给用户加硬币的
		 */
		VideoInfo dbVideoInfo = videoInfoMapper.selectByVideoId(videoId);
		if (ObjectUtils.isEmpty(dbVideoInfo)) {
			// 这里需要给用户加硬币
			SysSettingDto sysSetting = redisComponent.getSysSetting();
			// 给用户加硬币
			userInfoMapper.updateCoinCountInfo(videoInfoPost.getUserId(), sysSetting.getPostVideoCoinCount());
		}
		/**
		 * 拷贝对象到查看表中
		 */
		VideoInfo videoInfo = CopyTools.copy(videoInfoPost, VideoInfo.class);
		videoInfoMapper.insertOrUpdate(videoInfo);

		/**
		 * 将正式表中的文件信息先删除，然后再插入，简单，不需要一个一个对比再修改
		 */
		VideoInfoFileQuery videoInfoFileQuery = new VideoInfoFileQuery();
		videoInfoFileQuery.setVideoId(videoId);
		videoInfoFileMapper.deleteByParam(videoInfoFileQuery);

		VideoInfoFilePostQuery videoInfoFilePostQuery = new VideoInfoFilePostQuery();
		videoInfoFilePostQuery.setVideoId(videoId);
		List<VideoInfoFilePost> videoInfoFilePosts = videoInfoFilePostMapper.selectList(videoInfoFilePostQuery);

		List<VideoInfoFile> videoInfoFiles = CopyTools.copyList(videoInfoFilePosts, VideoInfoFile.class);
		videoInfoFileMapper.insertBatch(videoInfoFiles);

		/**
		 * 删除文件：如果是修改后再审核，如果用户删除了原来的视频，需要从redis中取出删除文件的路径集合
		 */
		List<String> delFilePathList = redisComponent.getDelFileList(videoId);
		if (delFilePathList != null && !delFilePathList.isEmpty()) {
			for (String delFilePath : delFilePathList) {
				File file = new File(appConfig.getProjectFolder() + Constants.FILE_FOLDER + delFilePath);
				if (file.exists()) {
                    try {
                        FileUtils.deleteDirectory(file);
                    } catch (IOException e) {
                        log.error("删除文件失败", e);
                    }
                }
			}
		}

		/**
		 * 删除redis中的记录
		 */
		redisComponent.clearDeleteFileList(videoId);

		// 审核通过后保存信息到ES里头，其实也可以用定时任务查询，因为有create_time和update_time字段，可以根据这两个字段来判断是否需要更新ES
		esSearchComponent.saveDoc(videoInfo);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void changeVideoInteraction(String userId, String videoId, String interaction) {
		/**
		 * 开启，关闭评论不需要审核，直接更新即可
		 */
		VideoInfoPost videoInfoPost = videoInfoPostMapper.selectByVideoId(videoId);
		if (!Objects.equals(userId, videoInfoPost.getUserId())) {
			throw new BusinessException(ResponseCodeEnum.CODE_600);
		}
		VideoInfoPost updateInfo = new VideoInfoPost();
		updateInfo.setInteraction(interaction);
		videoInfoPostMapper.updateByVideoId(updateInfo, videoId);

		// 正式表中也更新
		VideoInfo updateVideoInfo = new VideoInfo();
		updateVideoInfo.setInteraction(interaction);
		videoInfoMapper.updateByVideoId(updateVideoInfo, videoId);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void deleteVideoInfo(String userId, String videoId) {
		VideoInfoPost videoInfoPost = videoInfoPostMapper.selectByVideoId(videoId);
		if (videoInfoPost != null && userId != null && !Objects.equals(userId, videoInfoPost.getUserId())) {
			throw new BusinessException(ResponseCodeEnum.CODE_600);
		}
		// 删除视频信息
		videoInfoPostMapper.deleteByVideoId(videoId);

		// 正式表中也删除
		videoInfoMapper.deleteByVideoId(videoId);


		// 硬币减少
		SysSettingDto sysSetting = redisComponent.getSysSetting();
		if (!StringTools.isEmpty(userId)) {
			userInfoMapper.updateCoinCountInfo(userId, -sysSetting.getPostVideoCoinCount());
		}

		// 删除ES中的记录
		esSearchComponent.deleteDoc(videoId);
		/**
		 * 用线程池异步删除文件
		 */
		executorService.execute(() -> {
			// 删除视频文件信息
			VideoInfoFilePostQuery videoInfoFilePostQuery = new VideoInfoFilePostQuery();
			videoInfoFilePostQuery.setUserId(userId);
			videoInfoFilePostQuery.setVideoId(videoId);
			List<VideoInfoFilePost> filePostList = videoInfoFilePostMapper.selectList(videoInfoFilePostQuery);
			videoInfoFilePostMapper.deleteByParam(videoInfoFilePostQuery);

			VideoInfoFileQuery videoInfoFileQuery = new VideoInfoFileQuery();
			videoInfoFileQuery.setVideoId(videoId);
			videoInfoFileQuery.setUserId(userId);
			videoInfoFileMapper.deleteByParam(videoInfoFileQuery);

			// 删除弹幕，评论
			VideoDanmuQuery videoDanmuQuery = new VideoDanmuQuery();
			videoDanmuQuery.setVideoId(videoId);
			videoDanmuMapper.deleteByParam(videoDanmuQuery);

			VideoCommentQuery videoCommentQuery = new VideoCommentQuery();
			videoCommentQuery.setVideoId(videoId);
			videoCommentMapper.deleteByParam(videoCommentQuery);

			// 删除文件
			if (!filePostList.isEmpty()) {
				for (VideoInfoFilePost item : filePostList) {
                    try {
                        FileUtils.deleteDirectory(new File(appConfig.getProjectFolder() + item.getFilePath()));
                    } catch (IOException e) {
                        log.error("删除文件失败", e);
						log.error("文件路径", item.getFilePath());
                    }
                }
			}
		});
	}

	/**
	 * 增加播放量
	 * @param videoId
	 */
	@Override
	public void addReadCount(String videoId) {
		videoInfoMapper.updateCountInfo(videoId, UserActionTypeEnum.VIDEO_PLAY.getField(), Constants.ONE);
	}

	@Override
	public void updateRecommendType(String videoId) {
		VideoInfo videoInfo = videoInfoMapper.selectByVideoId(videoId);
		if (videoInfo == null) {
			throw new BusinessException(ResponseCodeEnum.CODE_600);
		}
		VideoRecommendTypeEnum recommendTypeEnum = VideoRecommendTypeEnum.getEnumByCode(videoInfo.getRecommendType());
		if (recommendTypeEnum == null) {
			throw new BusinessException(ResponseCodeEnum.CODE_600);
		}
		VideoInfoQuery query = new VideoInfoQuery();
		VideoInfo updateInfo = new VideoInfo();
		query.setVideoId(videoId);
		switch (recommendTypeEnum) {
			case RECOMMEND:
				updateInfo.setRecommendType(VideoRecommendTypeEnum.NOT_RECOMMEND.getCode());
				query.setRecommendType(VideoRecommendTypeEnum.RECOMMEND.getCode());
				break;
			case NOT_RECOMMEND:
				updateInfo.setRecommendType(VideoRecommendTypeEnum.RECOMMEND.getCode());
				query.setRecommendType(VideoRecommendTypeEnum.NOT_RECOMMEND.getCode());
				break;
		}

		videoInfoMapper.updateByParam(updateInfo, query);
	}
}